/*
 * Copyright (c) 2022, IMMORTA Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * - Neither the name of IMMORTA Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*!
 * @file hsm_drv.c
 * @brief This file defines hsm driver functions
 */

/*******Includes***************************************************************/
#include <stdbool.h>
#include "hsm_reg_access.h"
#include "hsm_drv.h"

/*******Definitions************************************************************/
/* Command header sequence */
#define CMD_HEAD_SEQUENCE          (0xC415A500UL)
/* Command response time out */
#define CMD_RESPONSE_TIME_OUT      (4000000UL)

/*******Variables**************************************************************/
/* Pointer to runtime state structure */
static hsm_state_t* s_hsmState = NULL;

/*******Prototypes*************************************************************/

/*******Private Functions******************************************************/
static errcode_t HSM_GetCommandExecStatus(void);
static void FLASH_SafetyLock(void);
static void FLASH_SafetyUnlock(void);

/*******Code*******************************************************************/

/*******************************************************************************
 *** Function Name : HSM_Init
 *** Description   : Initializes the HSM Interrupt and semaphore
 ******************************************************************************/
errcode_t HSM_Init(hsm_state_t* state)
{
    ASSERT_PARAM(state != NULL);

    errcode_t errCode;
    s_hsmState = state;

    errCode = OS_Adapter_CreateSemaphore(&(s_hsmState->cmdSemaphore), 0U);

    if (errCode == ERR_SUCCESS) {
        NVIC_EnableIRQ(MBOX_IRQn);
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : HSM_DeInit
 *** Description   : Deinitializes the HSM module
 ******************************************************************************/
void HSM_DeInit(void)
{
    ASSERT_PARAM(s_hsmState != NULL);

    MBOX_REG_CommandIntEnable(false);
    MBOX_REG_ClearCommandCompletionStatus();

    NVIC_DisableIRQ(MBOX_IRQn);

#ifdef USING_OS_FREERTOS
    if (s_hsmState->cmdSemaphore != NULL) {
#endif
        (void)OS_Adapter_DestroySemaphore(&(s_hsmState->cmdSemaphore));
#ifdef USING_OS_FREERTOS
        s_hsmState->cmdSemaphore = NULL;
    }
#endif

    s_hsmState->cmdInProgress = false;
    s_hsmState = NULL;
}

/*******************************************************************************
 *** Function Name : HSM_EncryptECB
 *** Description   : This function performs the AES-128/AES-256/SM4 encryption
 ***                 in EBC mode of the input cipher text buffer
 ******************************************************************************/
errcode_t HSM_EncryptECB(crypto_alg_t alg,
                         hsm_key_id_t keyId,
                         const uint8_t* plainText,
                         uint32_t length,
                         uint8_t* cipherText,
                         uint32_t timeout)
{
    ASSERT_PARAM(plainText != NULL);
    ASSERT_PARAM(cipherText != NULL);
    ASSERT_PARAM(s_hsmState != NULL);

    /* Not used, avoid warning */
    (void)timeout;
    errcode_t errCode = ERR_SUCCESS;
    uint32_t cmdBuf[6U];

    if (MBOX_REG_GetCommandIdleStatus() != true) {
        errCode = ERR_BUSY;
    } else {
        s_hsmState->cmdInProgress = true;

        cmdBuf[0] = CMD_HEAD_SEQUENCE + (uint32_t)HSM_CMD_ENC_ECB;
        cmdBuf[1] = (uint32_t)alg;
        cmdBuf[2] = (uint32_t)keyId;
        cmdBuf[3] = length;
        cmdBuf[4] = (uint32_t)plainText;
        cmdBuf[5] = (uint32_t)cipherText;

        s_hsmState->cmd = HSM_CMD_ENC_ECB;

        MBOX_REG_CommandIntEnable(true);
        MBOX_REG_WriteCommand(cmdBuf, sizeof(cmdBuf));
        MBOX_REG_StartCommand();

        /* Wait for the command to complete */
        errCode = OS_Adapter_WaitSemaphore(&(s_hsmState->cmdSemaphore), OS_ADAPTER_WAIT_FOREVER);

        if (errCode == ERR_SUCCESS) {
            errCode = HSM_GetCommandExecStatus();
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : HSM_DecryptECB
 *** Description   : This function performs the AES-128 decryption in ECB mode
 ***                  of the input cipher text buffer
 ******************************************************************************/
errcode_t HSM_DecryptECB(crypto_alg_t alg,
                         hsm_key_id_t keyId,
                         const uint8_t* cipherText,
                         uint32_t length,
                         uint8_t* plainText,
                         uint32_t timeout)
{
    ASSERT_PARAM(cipherText != NULL);
    ASSERT_PARAM(plainText != NULL);
    ASSERT_PARAM(s_hsmState != NULL);

    /* Not used, avoid warning */
    (void)timeout;
    errcode_t errCode = ERR_SUCCESS;
    uint32_t cmdBuf[6U];

    if (MBOX_REG_GetCommandIdleStatus() != true) {
        errCode = ERR_BUSY;
    } else {
        s_hsmState->cmdInProgress = true;

        cmdBuf[0] = CMD_HEAD_SEQUENCE + (uint32_t)HSM_CMD_DEC_ECB;
        cmdBuf[1] = (uint32_t)alg;
        cmdBuf[2] = (uint32_t)keyId;
        cmdBuf[3] = length;
        cmdBuf[4] = (uint32_t)plainText;
        cmdBuf[5] = (uint32_t)cipherText;

        s_hsmState->cmd = HSM_CMD_DEC_ECB;

        MBOX_REG_CommandIntEnable(true);
        MBOX_REG_WriteCommand(cmdBuf, sizeof(cmdBuf));
        MBOX_REG_StartCommand();

        /* Wait for the command to complete */
        errCode = OS_Adapter_WaitSemaphore(&(s_hsmState->cmdSemaphore), OS_ADAPTER_WAIT_FOREVER);

        if (errCode == ERR_SUCCESS) {
            errCode = HSM_GetCommandExecStatus();
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : HSM_EncryptCBC
 *** Description   : This function performs the AES-128/AES-256/SM4 encryption
 ***                 in CBC mode of the input cipher text buffer
 ******************************************************************************/
errcode_t HSM_EncryptCBC(crypto_alg_t alg,
                         hsm_key_id_t keyId,
                         const uint8_t* plainText,
                         uint32_t length,
                         const uint8_t* iv,
                         uint8_t* cipherText,
                         uint32_t timeout)
{
    ASSERT_PARAM(plainText != NULL);
    ASSERT_PARAM(cipherText != NULL);
    ASSERT_PARAM(iv != NULL);
    ASSERT_PARAM(s_hsmState != NULL);

    /* Not used, avoid warning */
    (void)timeout;
    errcode_t errCode = ERR_SUCCESS;
    uint32_t cmdBuf[7U];

    if (MBOX_REG_GetCommandIdleStatus() != true) {
        errCode = ERR_BUSY;
    } else {
        s_hsmState->cmdInProgress = true;

        cmdBuf[0] = CMD_HEAD_SEQUENCE + (uint32_t)HSM_CMD_ENC_CBC;
        cmdBuf[1] = (uint32_t)alg;
        cmdBuf[2] = (uint32_t)keyId;
        cmdBuf[3] = length;
        cmdBuf[4] = (uint32_t)plainText;
        cmdBuf[5] = (uint32_t)cipherText;
        cmdBuf[6] = (uint32_t)iv;

        s_hsmState->cmd = HSM_CMD_ENC_CBC;

        MBOX_REG_CommandIntEnable(true);
        MBOX_REG_WriteCommand(cmdBuf, sizeof(cmdBuf));
        MBOX_REG_StartCommand();

        /* Wait for the command to complete */
        errCode = OS_Adapter_WaitSemaphore(&(s_hsmState->cmdSemaphore), OS_ADAPTER_WAIT_FOREVER);

        if (errCode == ERR_SUCCESS) {
            errCode = HSM_GetCommandExecStatus();
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : HSM_DecryptCBC
 *** Description   : This function performs the AES-128 decryption in CBC mode
 ***                 of the input cipher text buffer
 ******************************************************************************/
errcode_t HSM_DecryptCBC(crypto_alg_t alg,
                         hsm_key_id_t keyId,
                         const uint8_t* cipherText,
                         uint32_t length,
                         const uint8_t* iv,
                         uint8_t* plainText,
                         uint32_t timeout)
{
    ASSERT_PARAM(plainText != NULL);
    ASSERT_PARAM(cipherText != NULL);
    ASSERT_PARAM(iv != NULL);
    ASSERT_PARAM(s_hsmState != NULL);

    /* Not used, avoid warning */
    (void)timeout;
    errcode_t errCode = ERR_SUCCESS;
    uint32_t cmdBuf[7U];

    if (MBOX_REG_GetCommandIdleStatus() != true) {
        errCode = ERR_BUSY;
    } else {
        s_hsmState->cmdInProgress = true;

        cmdBuf[0] = CMD_HEAD_SEQUENCE + (uint32_t)HSM_CMD_DEC_CBC;
        cmdBuf[1] = (uint32_t)alg;
        cmdBuf[2] = (uint32_t)keyId;
        cmdBuf[3] = length;
        cmdBuf[4] = (uint32_t)plainText;
        cmdBuf[5] = (uint32_t)cipherText;
        cmdBuf[6] = (uint32_t)iv;

        s_hsmState->cmd = HSM_CMD_DEC_CBC;

        MBOX_REG_CommandIntEnable(true);
        MBOX_REG_WriteCommand(cmdBuf, sizeof(cmdBuf));
        MBOX_REG_StartCommand();

        /* Wait for the command to complete */
        errCode = OS_Adapter_WaitSemaphore(&(s_hsmState->cmdSemaphore), OS_ADAPTER_WAIT_FOREVER);

        if (errCode == ERR_SUCCESS) {
            errCode = HSM_GetCommandExecStatus();
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : HSM_GenerateMAC
 *** Description   : This function calculates the MAC of a given message using
 ***                 CMAC with AES-128/AES-256/SM4
 ******************************************************************************/
errcode_t HSM_GenerateMAC(crypto_alg_t alg,
                          hsm_key_id_t keyId,
                          const uint8_t* msg,
                          uint32_t msgLen,
                          uint8_t* cmac,
                          uint32_t timeout)
{
    ASSERT_PARAM(msg != NULL);
    ASSERT_PARAM(cmac != NULL);
    ASSERT_PARAM(s_hsmState != NULL);

    /* Not used, avoid warning */
    (void)timeout;
    errcode_t errCode = ERR_SUCCESS;
    uint32_t cmdBuf[6U];

    if (MBOX_REG_GetCommandIdleStatus() != true) {
        errCode = ERR_BUSY;
    } else {
        s_hsmState->cmdInProgress = true;

        cmdBuf[0] = CMD_HEAD_SEQUENCE + (uint32_t)HSM_CMD_GENERATE_MAC;
        cmdBuf[1] = (uint32_t)alg;
        cmdBuf[2] = (uint32_t)keyId;
        cmdBuf[3] = (uint32_t)msg;
        cmdBuf[4] = msgLen;
        cmdBuf[5] = (uint32_t)cmac;

        s_hsmState->cmd = HSM_CMD_GENERATE_MAC;

        MBOX_REG_CommandIntEnable(true);
        MBOX_REG_WriteCommand(cmdBuf, sizeof(cmdBuf));
        MBOX_REG_StartCommand();

        /* Wait for the command to complete */
        errCode = OS_Adapter_WaitSemaphore(&(s_hsmState->cmdSemaphore), OS_ADAPTER_WAIT_FOREVER);

        if (errCode == ERR_SUCCESS) {
            errCode = HSM_GetCommandExecStatus();
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : HSM_VerifyMAC
 *** Description   : This function verifies the MAC of a given message using
 ***                 CMAC with AES-128/AES-256/SM4
 ******************************************************************************/
errcode_t HSM_VerifyMAC(crypto_alg_t alg,
                        hsm_key_id_t keyId,
                        const uint8_t* msg,
                        uint32_t msgLen,
                        const uint8_t* mac,
                        uint8_t macLen,
                        bool* verifyStatus,
                        uint32_t timeout)
{
    ASSERT_PARAM(msg != NULL);
    ASSERT_PARAM(mac != NULL);
    ASSERT_PARAM(verifyStatus != NULL);
    ASSERT_PARAM(s_hsmState != NULL);

    /* Not used, avoid warning */
    (void)timeout;
    errcode_t errCode = ERR_SUCCESS;
    uint32_t cmdBuf[8U];

    if (MBOX_REG_GetCommandIdleStatus() != true) {
        errCode = ERR_BUSY;
    } else {

        s_hsmState->cmdInProgress = true;

        cmdBuf[0] = CMD_HEAD_SEQUENCE + (uint32_t)HSM_CMD_VERIFY_MAC;
        cmdBuf[1] = (uint32_t)alg;
        cmdBuf[2] = (uint32_t)keyId;
        cmdBuf[3] = (uint32_t)msg;
        cmdBuf[4] = msgLen;
        cmdBuf[5] = (uint32_t)mac;
        cmdBuf[6] = (uint32_t)macLen;
        cmdBuf[7] = (uint32_t)verifyStatus;

        s_hsmState->cmd = HSM_CMD_VERIFY_MAC;

        MBOX_REG_CommandIntEnable(true);
        MBOX_REG_WriteCommand(cmdBuf, sizeof(cmdBuf));
        MBOX_REG_StartCommand();

        /* Wait for the command to complete */
        errCode = OS_Adapter_WaitSemaphore(&(s_hsmState->cmdSemaphore), OS_ADAPTER_WAIT_FOREVER);

        if (errCode == ERR_SUCCESS) {
            errCode = HSM_GetCommandExecStatus();
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : HSM_EnableRND
 *** Description   : The function set random enable or disable
 ******************************************************************************/
errcode_t HSM_EnableRND(bool enable)
{
    ASSERT_PARAM(s_hsmState != NULL);

    errcode_t errCode = ERR_SUCCESS;
    uint32_t cmdBuf[2U];
    volatile uint32_t timeout = CMD_RESPONSE_TIME_OUT;

    if (MBOX_REG_GetCommandIdleStatus() != true) {
        errCode = ERR_BUSY;
    } else {
        s_hsmState->cmdInProgress = true;

        cmdBuf[0] = CMD_HEAD_SEQUENCE + (uint32_t)HSM_CMD_INIT_RNG;
        cmdBuf[1] = (uint32_t)enable;

        s_hsmState->cmd = HSM_CMD_INIT_RNG;

        /* Use polling to get command completion status without interrupts */
        MBOX_REG_CommandIntEnable(false);
        MBOX_REG_WriteCommand(cmdBuf, sizeof(cmdBuf));
        MBOX_REG_StartCommand();

        /* Wait for the command to complete */
        while ((MBOX_REG_GetCommandCompletionStatus() != true) && (timeout > 0)) {
            timeout--;
        }

        MBOX_REG_ClearCommandCompletionStatus();

        if (timeout == 0U) {
            errCode = ERR_TIMEOUT;
        } else {
            errCode = HSM_GetCommandExecStatus();
        }

        s_hsmState->cmdInProgress = false;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : HSM_GenerateRND
 *** Description   : The function returns up to 256 random bits
 ******************************************************************************/
errcode_t HSM_GenerateRND(uint8_t* rnd, uint32_t len)
{
    ASSERT_PARAM(s_hsmState != NULL);
    ASSERT_PARAM(rnd != NULL);

    errcode_t errCode = ERR_SUCCESS;
    uint32_t cmdBuf[3U];
    volatile uint32_t timeout = CMD_RESPONSE_TIME_OUT;

    if (MBOX_REG_GetCommandIdleStatus() != true) {
        errCode = ERR_BUSY;
    } else {
        s_hsmState->cmdInProgress = true;

        cmdBuf[0] = CMD_HEAD_SEQUENCE + (uint32_t)HSM_CMD_RND;
        cmdBuf[1] = (uint32_t)rnd;
        cmdBuf[2] = (uint32_t)len;

        s_hsmState->cmd = HSM_CMD_RND;

        /* Use polling to get command completion status without interrupts */
        MBOX_REG_CommandIntEnable(false);
        MBOX_REG_WriteCommand(cmdBuf, sizeof(cmdBuf));
        MBOX_REG_StartCommand();

        /* Wait for the command to complete */
        while ((MBOX_REG_GetCommandCompletionStatus() != true) && (timeout > 0)) {
            timeout--;
        }

        MBOX_REG_ClearCommandCompletionStatus();

        if (timeout == 0U) {
            errCode = ERR_TIMEOUT;
        } else {
            errCode = HSM_GetCommandExecStatus();
        }

        s_hsmState->cmdInProgress = false;
    }

    return errCode;
}


/*******************************************************************************
 *** Function Name : HSM_LoadKey
 *** Description   : This function updates an internal key (128bits)per the SHE
 ***                 specification
 ******************************************************************************/
errcode_t HSM_LoadKey(hsm_key_id_t keyId,
                      const uint8_t* m1,
                      const uint8_t* m2,
                      const uint8_t* m3,
                      uint8_t* m4,
                      uint8_t* m5)
{
    ASSERT_PARAM(m1 != NULL);
    ASSERT_PARAM(m2 != NULL);
    ASSERT_PARAM(m3 != NULL);
    ASSERT_PARAM(m4 != NULL);
    ASSERT_PARAM(m5 != NULL);
    ASSERT_PARAM(s_hsmState != NULL);

    errcode_t errCode = ERR_SUCCESS;
    uint32_t cmdBuf[7U];
    volatile uint32_t timeout = CMD_RESPONSE_TIME_OUT;

    if (MBOX_REG_GetCommandIdleStatus() != true) {
        errCode = ERR_BUSY;
    } else {
        s_hsmState->cmdInProgress = true;

        cmdBuf[0] = CMD_HEAD_SEQUENCE + (uint32_t)HSM_CMD_LOAD_KEY;
        cmdBuf[1] = (uint32_t)keyId;
        cmdBuf[2] = (uint32_t)m1;
        cmdBuf[3] = (uint32_t)m2;
        cmdBuf[4] = (uint32_t)m3;
        cmdBuf[5] = (uint32_t)m4;
        cmdBuf[6] = (uint32_t)m5;

        s_hsmState->cmd = HSM_CMD_LOAD_KEY;

        MBOX_REG_CommandIntEnable(false);
        MBOX_REG_WriteCommand(cmdBuf, sizeof(cmdBuf));
        FLASH_SafetyUnlock();
        MBOX_REG_StartCommand();

        /* Wait for the command to complete */
        while ((MBOX_REG_GetCommandCompletionStatus() != true) && (timeout > 0)) {
            timeout--;
        }

        FLASH_SafetyLock();

        MBOX_REG_ClearCommandCompletionStatus();

        if (timeout == 0U) {
            errCode = ERR_TIMEOUT;
        } else {
            errCode = HSM_GetCommandExecStatus();
        }

        s_hsmState->cmdInProgress = false;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : HSM_LoadKeyExtend
 *** Description   : This function updates an internal key (256bits extend)per
 ***                 the SHE specification
 ******************************************************************************/
errcode_t HSM_LoadKeyExtend(hsm_key_id_t keyId,
                            const uint8_t* m1,
                            const uint8_t* m2,
                            const uint8_t* m3,
                            uint8_t* m4,
                            uint8_t* m5)
{
    ASSERT_PARAM(m1 != NULL);
    ASSERT_PARAM(m2 != NULL);
    ASSERT_PARAM(m3 != NULL);
    ASSERT_PARAM(m4 != NULL);
    ASSERT_PARAM(m5 != NULL);
    ASSERT_PARAM(s_hsmState != NULL);

    errcode_t errCode = ERR_SUCCESS;
    uint32_t cmdBuf[7U];
    volatile uint32_t timeout = CMD_RESPONSE_TIME_OUT;

    if (MBOX_REG_GetCommandIdleStatus() != true) {
        errCode = ERR_BUSY;
    } else {
        s_hsmState->cmdInProgress = true;

        cmdBuf[0] = CMD_HEAD_SEQUENCE + (uint32_t)HSM_CMD_LOAD_KEY_EXTEND;
        cmdBuf[1] = (uint32_t)keyId;
        cmdBuf[2] = (uint32_t)m1;
        cmdBuf[3] = (uint32_t)m2;
        cmdBuf[4] = (uint32_t)m3;
        cmdBuf[5] = (uint32_t)m4;
        cmdBuf[6] = (uint32_t)m5;

        s_hsmState->cmd = HSM_CMD_LOAD_KEY_EXTEND;

        MBOX_REG_CommandIntEnable(false);
        MBOX_REG_WriteCommand(cmdBuf, sizeof(cmdBuf));
        FLASH_SafetyUnlock();
        MBOX_REG_StartCommand();

        /* Wait for the command to complete */
        while ((MBOX_REG_GetCommandCompletionStatus() != true) && (timeout > 0)) {
            timeout--;
        }

        FLASH_SafetyLock();

        MBOX_REG_ClearCommandCompletionStatus();

        if (timeout == 0U) {
            errCode = ERR_TIMEOUT;
        } else {
            errCode = HSM_GetCommandExecStatus();
        }

        s_hsmState->cmdInProgress = false;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : HSM_LoadPlainKey
 *** Description   : Updates the RAM key memory slot with a 128-bit key value
 ******************************************************************************/
errcode_t HSM_LoadPlainKey(const uint8_t* plainKey)
{
    ASSERT_PARAM(plainKey != NULL);
    ASSERT_PARAM(s_hsmState != NULL);

    errcode_t errCode;
    uint32_t cmdBuf[2U];
    volatile uint32_t timeout = CMD_RESPONSE_TIME_OUT;

    if (MBOX_REG_GetCommandIdleStatus() != true) {
        errCode = ERR_BUSY;
    } else {
        s_hsmState->cmdInProgress = true;

        cmdBuf[0] = CMD_HEAD_SEQUENCE + (uint32_t)HSM_CMD_LOAD_PLAIN_KEY;
        cmdBuf[1] = (uint32_t)plainKey;

        s_hsmState->cmd = HSM_CMD_LOAD_PLAIN_KEY;

        /* Use polling to get command completion status without interrupts */
        MBOX_REG_CommandIntEnable(false);
        MBOX_REG_WriteCommand(cmdBuf, sizeof(cmdBuf));
        MBOX_REG_StartCommand();

        /* Wait for the command to complete */
        while ((MBOX_REG_GetCommandCompletionStatus() != true) && (timeout > 0)) {
            timeout--;
        }

        MBOX_REG_ClearCommandCompletionStatus();

        if (timeout == 0U) {
            errCode = ERR_TIMEOUT;
        } else {
            errCode = HSM_GetCommandExecStatus();
        }

        s_hsmState->cmdInProgress = false;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : HSM_LoadPlainKeyExtend
 *** Description   : Updates the RAM key memory slot with a 256-bit(extend)
 ***                 key value
 ******************************************************************************/
errcode_t HSM_LoadPlainKeyExtend(const uint8_t* plainKey)
{
    ASSERT_PARAM(plainKey != NULL);
    ASSERT_PARAM(s_hsmState != NULL);

    errcode_t errCode;
    uint32_t cmdBuf[2U];
    volatile uint32_t timeout = CMD_RESPONSE_TIME_OUT;

    if (MBOX_REG_GetCommandIdleStatus() != true) {
        errCode = ERR_BUSY;
    } else {
        s_hsmState->cmdInProgress = true;

        cmdBuf[0] = CMD_HEAD_SEQUENCE + (uint32_t)HSM_CMD_LOAD_PLAIN_KEY_EXTEND;
        cmdBuf[1] = (uint32_t)plainKey;

        s_hsmState->cmd = HSM_CMD_LOAD_PLAIN_KEY;

        /* Use polling to get command completion status without interrupts */
        MBOX_REG_CommandIntEnable(false);
        MBOX_REG_WriteCommand(cmdBuf, sizeof(cmdBuf));
        MBOX_REG_StartCommand();

        /* Wait for the command to complete */
        while ((MBOX_REG_GetCommandCompletionStatus() != true) && (timeout > 0)) {
            timeout--;
        }

        MBOX_REG_ClearCommandCompletionStatus();

        if (timeout == 0U) {
            errCode = ERR_TIMEOUT;
        } else {
            errCode = HSM_GetCommandExecStatus();
        }

        s_hsmState->cmdInProgress = false;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : HSM_ExportRAMKey
 *** Description   : This function exports the RAM_KEY(128bits) into a format
 ***                 protected by SECRET_KEY
 ******************************************************************************/
errcode_t HSM_ExportRAMKey(uint8_t* m1, uint8_t* m2, uint8_t* m3,
                           uint8_t* m4, uint8_t* m5)
{
    ASSERT_PARAM(m1 != NULL);
    ASSERT_PARAM(m2 != NULL);
    ASSERT_PARAM(m3 != NULL);
    ASSERT_PARAM(m4 != NULL);
    ASSERT_PARAM(m5 != NULL);
    ASSERT_PARAM(s_hsmState != NULL);

    errcode_t errCode;
    uint32_t cmdBuf[6U];
    volatile uint32_t timeout = CMD_RESPONSE_TIME_OUT;

    if (MBOX_REG_GetCommandIdleStatus() != true) {
        errCode = ERR_BUSY;
    } else {
        s_hsmState->cmdInProgress = true;

        cmdBuf[0] = CMD_HEAD_SEQUENCE + (uint32_t)HSM_CMD_EXPORT_RAM_KEY;
        cmdBuf[1] = (uint32_t)m1;
        cmdBuf[2] = (uint32_t)m2;
        cmdBuf[3] = (uint32_t)m3;
        cmdBuf[4] = (uint32_t)m4;
        cmdBuf[5] = (uint32_t)m5;

        s_hsmState->cmd = HSM_CMD_EXPORT_RAM_KEY;

        /* Use polling to get command completion status without interrupts */
        MBOX_REG_CommandIntEnable(false);
        MBOX_REG_WriteCommand(cmdBuf, sizeof(cmdBuf));
        MBOX_REG_StartCommand();

        /* Wait for the command to complete */
        while ((MBOX_REG_GetCommandCompletionStatus() != true) && (timeout > 0)) {
            timeout--;
        }

        MBOX_REG_ClearCommandCompletionStatus();

        if (timeout == 0U) {
            errCode = ERR_TIMEOUT;
        } else {
            errCode = HSM_GetCommandExecStatus();
        }

        s_hsmState->cmdInProgress = false;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : HSM_ExportRAMKey
 *** Description   : This function exports the RAM_KEY(256bits extend) into a
 ***                 format protected by SECRET_KEY
 ******************************************************************************/
errcode_t HSM_ExportRAMKeyExtend(uint8_t* m1, uint8_t* m2, uint8_t* m3,
                                 uint8_t* m4, uint8_t* m5)
{
    ASSERT_PARAM(m1 != NULL);
    ASSERT_PARAM(m2 != NULL);
    ASSERT_PARAM(m3 != NULL);
    ASSERT_PARAM(m4 != NULL);
    ASSERT_PARAM(m5 != NULL);
    ASSERT_PARAM(s_hsmState != NULL);

    errcode_t errCode;
    uint32_t cmdBuf[6U];
    volatile uint32_t timeout = CMD_RESPONSE_TIME_OUT;

    if (MBOX_REG_GetCommandIdleStatus() != true) {
        errCode = ERR_BUSY;
    } else {
        s_hsmState->cmdInProgress = true;

        cmdBuf[0] = CMD_HEAD_SEQUENCE + (uint32_t)HSM_CMD_EXPORT_RAM_KEY_EXTEND;
        cmdBuf[1] = (uint32_t)m1;
        cmdBuf[2] = (uint32_t)m2;
        cmdBuf[3] = (uint32_t)m3;
        cmdBuf[4] = (uint32_t)m4;
        cmdBuf[5] = (uint32_t)m5;

        s_hsmState->cmd = HSM_CMD_EXPORT_RAM_KEY_EXTEND;

        /* Use polling to get command completion status without interrupts */
        MBOX_REG_CommandIntEnable(false);
        MBOX_REG_WriteCommand(cmdBuf, sizeof(cmdBuf));
        MBOX_REG_StartCommand();

        /* Wait for the command to complete */
        while ((MBOX_REG_GetCommandCompletionStatus() != true) && (timeout > 0)) {
            timeout--;
        }

        MBOX_REG_ClearCommandCompletionStatus();

        if (timeout == 0U) {
            errCode = ERR_TIMEOUT;
        } else {
            errCode = HSM_GetCommandExecStatus();
        }

        s_hsmState->cmdInProgress = false;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : HSM_MPCompress
 *** Description   : This function set to compress the given messages
 ******************************************************************************/
errcode_t HSM_MPCompress(const uint8_t* msg,
                         uint16_t msgLen,
                         uint8_t* mpCompress,
                         uint32_t timeout)
{
    ASSERT_PARAM(msg != NULL);
    ASSERT_PARAM(mpCompress != NULL);
    ASSERT_PARAM(s_hsmState != NULL);

    /* Not used, avoid warning */
    (void)timeout;
    errcode_t errCode = ERR_SUCCESS;
    uint32_t cmdBuf[4U];

    if (MBOX_REG_GetCommandIdleStatus() != true) {
        errCode = ERR_BUSY;
    } else {
        s_hsmState->cmdInProgress = true;

        cmdBuf[0] = CMD_HEAD_SEQUENCE + (uint32_t)HSM_CMD_MP_COMPRESS;
        cmdBuf[1] = (uint32_t)msg;
        cmdBuf[2] = msgLen;
        cmdBuf[3] = (uint32_t)mpCompress;

        s_hsmState->cmd = HSM_CMD_MP_COMPRESS;

        MBOX_REG_CommandIntEnable(true);
        MBOX_REG_WriteCommand(cmdBuf, sizeof(cmdBuf));
        MBOX_REG_StartCommand();

        /* Wait for the command to complete */
        errCode = OS_Adapter_WaitSemaphore(&(s_hsmState->cmdSemaphore), OS_ADAPTER_WAIT_FOREVER);

        if (errCode == ERR_SUCCESS) {
            errCode = HSM_GetCommandExecStatus();
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : HSM_BootDefine
 *** Description   : The function implements an extension of the SHE standard to
 ***                 define both the user boot size and boot method
 ******************************************************************************/
errcode_t HSM_BootDefine(crypto_alg_t alg,
                         uint32_t bootSize,
                         hsm_boot_type_t bootType)
{
    ASSERT_PARAM(s_hsmState != NULL);

    errcode_t errCode = ERR_SUCCESS;
    uint32_t cmdBuf[4U];
    volatile uint32_t timeout = CMD_RESPONSE_TIME_OUT;

    if (MBOX_REG_GetCommandIdleStatus() != true) {
        errCode = ERR_BUSY;
    } else {
        s_hsmState->cmdInProgress = true;

        cmdBuf[0] = CMD_HEAD_SEQUENCE + (uint32_t)HSM_CMD_BOOT_DEFINE;
        cmdBuf[1] =  bootSize;
        cmdBuf[2] = (uint32_t)bootType;
        cmdBuf[3] = (uint32_t)alg;

        s_hsmState->cmd = HSM_CMD_BOOT_DEFINE;

        /* Use polling to get command completion status without interrupts */
        MBOX_REG_CommandIntEnable(false);
        MBOX_REG_WriteCommand(cmdBuf, sizeof(cmdBuf));
        FLASH_SafetyUnlock();
        MBOX_REG_StartCommand();

        /* Wait for the command to complete */
        while ((MBOX_REG_GetCommandCompletionStatus() != true) && (timeout > 0)) {
            timeout--;
        }

        FLASH_SafetyLock();

        MBOX_REG_ClearCommandCompletionStatus();

        if (timeout == 0U) {
            errCode = ERR_TIMEOUT;
        } else {
            errCode = HSM_GetCommandExecStatus();
        }

        s_hsmState->cmdInProgress = false;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : HSM_BootFailure
 *** Description   : The function is called during later stages of the boot
 ***                 process to detect a failure
 ******************************************************************************/
errcode_t HSM_BootFailure(void)
{
    ASSERT_PARAM(s_hsmState != NULL);

    errcode_t errCode = ERR_SUCCESS;
    uint32_t cmdBuf[1U];
    volatile uint32_t timeout = CMD_RESPONSE_TIME_OUT;

    if (MBOX_REG_GetCommandIdleStatus() != true) {
        errCode = ERR_BUSY;
    } else {
        s_hsmState->cmdInProgress = true;

        cmdBuf[0] = CMD_HEAD_SEQUENCE + (uint32_t)HSM_CMD_BOOT_FAILURE;

        s_hsmState->cmd = HSM_CMD_BOOT_FAILURE;

        /* Use polling to get command completion status without interrupts */
        MBOX_REG_CommandIntEnable(false);
        MBOX_REG_WriteCommand(cmdBuf, sizeof(cmdBuf));
        MBOX_REG_StartCommand();

        /* Wait for the command to complete */
        while ((MBOX_REG_GetCommandCompletionStatus() != true) && (timeout > 0)) {
            timeout--;
        }

        MBOX_REG_ClearCommandCompletionStatus();

        if (timeout == 0U) {
            errCode = ERR_TIMEOUT;
        } else {
            errCode = HSM_GetCommandExecStatus();
        }

        s_hsmState->cmdInProgress = false;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : HSM_BootOK
 *** Description   : The function is called during later stages of the boot
 ***                 process to mark successful boot verification
 ******************************************************************************/
errcode_t HSM_BootOK(void)
{
    ASSERT_PARAM(s_hsmState != NULL);

    errcode_t errCode = ERR_SUCCESS;
    uint32_t cmdBuf[1U];
    volatile uint32_t timeout = CMD_RESPONSE_TIME_OUT;

    if (MBOX_REG_GetCommandIdleStatus() != true) {
        errCode = ERR_BUSY;
    } else {
        s_hsmState->cmdInProgress = true;

        cmdBuf[0] = CMD_HEAD_SEQUENCE + (uint32_t)HSM_CMD_BOOT_OK;

        s_hsmState->cmd = HSM_CMD_BOOT_OK;

        /* Use polling to get command completion status without interrupts */
        MBOX_REG_CommandIntEnable(false);
        MBOX_REG_WriteCommand(cmdBuf, sizeof(cmdBuf));
        MBOX_REG_StartCommand();

        /* Wait for the command to complete */
        while ((MBOX_REG_GetCommandCompletionStatus() != true) && (timeout > 0)) {
            timeout--;
        }

        MBOX_REG_ClearCommandCompletionStatus();

        if (timeout == 0U) {
            errCode = ERR_TIMEOUT;
        } else {
            errCode = HSM_GetCommandExecStatus();
        }

        s_hsmState->cmdInProgress = false;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : HSM_DbgChal
 *** Description   : This function obtains a random number(128bits)
 ***                 which the user shall use along with the MASTER_ECU_KEY and
 ***                 UID to return an authorization request
 ******************************************************************************/
errcode_t HSM_DbgChal(uint8_t* challenge)
{
    ASSERT_PARAM(challenge != NULL);
    ASSERT_PARAM(s_hsmState != NULL);

    errcode_t errCode = ERR_SUCCESS;
    uint32_t cmdBuf[2U];
    volatile uint32_t timeout = CMD_RESPONSE_TIME_OUT;

    if (MBOX_REG_GetCommandIdleStatus() != true) {
        errCode = ERR_BUSY;
    } else {
        s_hsmState->cmdInProgress = true;

        cmdBuf[0] = CMD_HEAD_SEQUENCE + (uint32_t)HSM_CMD_DBG_CHAL;
        cmdBuf[1] = (uint32_t)challenge;

        s_hsmState->cmd = HSM_CMD_DBG_CHAL;

        /* Use polling to get command completion status without interrupts */
        MBOX_REG_CommandIntEnable(false);
        MBOX_REG_WriteCommand(cmdBuf, sizeof(cmdBuf));
        MBOX_REG_StartCommand();

        /* Wait for the command to complete */
        while ((MBOX_REG_GetCommandCompletionStatus() != true) && (timeout > 0)) {
            timeout--;
        }

        MBOX_REG_ClearCommandCompletionStatus();

        if (timeout == 0U) {
            errCode = ERR_TIMEOUT;
        } else {
            errCode = HSM_GetCommandExecStatus();
        }

        s_hsmState->cmdInProgress = false;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : HSM_DbgAuth
 *** Description   : This function erases all keys (actual and outdated) stored
 ***                 in NVM Memory if the authorization is confirmed by HSM
 ******************************************************************************/
errcode_t HSM_DbgAuth(const uint8_t* authorization)
{
    ASSERT_PARAM(authorization != NULL);
    ASSERT_PARAM(s_hsmState != NULL);

    errcode_t errCode = ERR_SUCCESS;
    uint32_t cmdBuf[2U];
    volatile uint32_t timeout = CMD_RESPONSE_TIME_OUT;

    if (MBOX_REG_GetCommandIdleStatus() != true) {
        errCode = ERR_BUSY;
    } else {
        s_hsmState->cmdInProgress = true;

        cmdBuf[0] = CMD_HEAD_SEQUENCE + (uint32_t)HSM_CMD_DBG_AUTH;
        cmdBuf[1] = (uint32_t)authorization;

        s_hsmState->cmd = HSM_CMD_DBG_AUTH;

        /* Use polling to get command completion status without interrupts */
        MBOX_REG_CommandIntEnable(false);
        MBOX_REG_WriteCommand(cmdBuf, sizeof(cmdBuf));
        FLASH_SafetyUnlock();
        MBOX_REG_StartCommand();

        /* Wait for the command to complete */
        while ((MBOX_REG_GetCommandCompletionStatus() != true) && (timeout > 0)) {
            timeout--;
        }

        FLASH_SafetyLock();

        MBOX_REG_ClearCommandCompletionStatus();

        if (timeout == 0U) {
            errCode = ERR_TIMEOUT;
        } else {
            errCode = HSM_GetCommandExecStatus();
        }

        s_hsmState->cmdInProgress = false;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : HSM_GetID
 *** Description   : This function returns the identity (UID) and the value of
 ***                 the status register protected by a MAC over a challenge
 ***                 and the data
 ******************************************************************************/
errcode_t HSM_GetID(const uint8_t* challenge,
                    uint8_t* uid,
                    uint8_t* sreg,
                    uint8_t* mac)
{
    ASSERT_PARAM(challenge != NULL);
    ASSERT_PARAM(uid != NULL);
    ASSERT_PARAM(sreg != NULL);
    ASSERT_PARAM(mac != NULL);
    ASSERT_PARAM(s_hsmState != NULL);

    errcode_t errCode = ERR_SUCCESS;
    uint32_t cmdBuf[5U];
    volatile uint32_t timeout = CMD_RESPONSE_TIME_OUT;

    if (MBOX_REG_GetCommandIdleStatus() != true) {
        errCode = ERR_BUSY;
    } else {
        s_hsmState->cmdInProgress = true;

        cmdBuf[0] = CMD_HEAD_SEQUENCE + (uint32_t)HSM_CMD_GET_ID;
        cmdBuf[1] = (uint32_t)challenge;
        cmdBuf[2] = (uint32_t)uid;
        cmdBuf[3] = (uint32_t)sreg;
        cmdBuf[4] = (uint32_t)mac;

        s_hsmState->cmd = HSM_CMD_GET_ID;

        /* Use polling to get command completion status without interrupts */
        MBOX_REG_CommandIntEnable(false);
        MBOX_REG_WriteCommand(cmdBuf, sizeof(cmdBuf));
        MBOX_REG_StartCommand();

        /* Wait for the command to complete */
        while ((MBOX_REG_GetCommandCompletionStatus() != true) && (timeout > 0)) {
            timeout--;
        }

        MBOX_REG_ClearCommandCompletionStatus();

        if (timeout == 0U) {
            errCode = ERR_TIMEOUT;
        } else {
            errCode = HSM_GetCommandExecStatus();
        }

        s_hsmState->cmdInProgress = false;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : HSM_GetStatus
 *** Description   : The function shall return the content of
 ***                 the SHE status register
 ******************************************************************************/
uint8_t HSM_GetStatus(void)
{
    uint8_t status;

    status = (uint8_t)(HSM_REG_GetFwStatus0() & 0xFFU);

    return status;
}

/*******************************************************************************
 *** Function Name : HSM_SetLifeCycle
 *** Description   : Set HSM life cycle
 ******************************************************************************/
errcode_t HSM_SetLifeCycle(life_cycle_t lc)
{
    ASSERT_PARAM(s_hsmState != NULL);

    errcode_t errCode = ERR_SUCCESS;
    uint32_t cmdBuf[2U];
    volatile uint32_t timeout = CMD_RESPONSE_TIME_OUT;

    if (MBOX_REG_GetCommandIdleStatus() != true) {
        errCode = ERR_BUSY;
    } else {
        s_hsmState->cmdInProgress = true;

        cmdBuf[0] = CMD_HEAD_SEQUENCE + (uint32_t)HSM_CMD_SET_LIFE_CYCLE;
        cmdBuf[1] = (uint32_t)lc;

        s_hsmState->cmd = HSM_CMD_SET_LIFE_CYCLE;

        /* Use polling to get command completion status without interrupts */
        MBOX_REG_CommandIntEnable(false);
        MBOX_REG_WriteCommand(cmdBuf, sizeof(cmdBuf));
        FLASH_SafetyUnlock();
        MBOX_REG_StartCommand();

        /* Wait for the command to complete */
        while ((MBOX_REG_GetCommandCompletionStatus() != true) && (timeout > 0)) {
            timeout--;
        }

        FLASH_SafetyLock();

        MBOX_REG_ClearCommandCompletionStatus();

        if (timeout == 0U) {
            errCode = ERR_TIMEOUT;
        } else {
            errCode = HSM_GetCommandExecStatus();
        }

        s_hsmState->cmdInProgress = false;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : HSM_GetLifeCycle
 *** Description   : Get HSM life cycle
 ******************************************************************************/
life_cycle_t HSM_GetLifeCycle(void)
{
    uint32_t status = HSM_REG_GetMboxStatus0();
    life_cycle_t lc;

    if (status & MBOX_STATUS0_LC_TEST_Msk) {
        lc = LIFE_CYCLE_TEST_MODE;
    } else if (status & MBOX_STATUS0_LC_DEV_Msk) {
        lc = LIFE_CYCLE_DEVELOP_MODE;
    } else if (status & MBOX_STATUS0_LC_MANU_Msk) {
        lc = LIFE_CYCLE_MANUFACTURE_MODE;
    } else if (status & MBOX_STATUS0_LC_USER_Msk) {
        lc = LIFE_CYCLE_USER_MODE;
    } else if (status & MBOX_STATUS0_LC_DEBG_Msk) {
        lc = LIFE_CYCLE_DEBUG_MODE;
    } else if (status & MBOX_STATUS0_LC_DEST_Msk) {
        lc = LIFE_CYCLE_DESTROY_MODE;
    } else {
        lc = LIFE_CYCLE_UNDEF_MODE;
    }

    return lc;
}

/*******************************************************************************
 *** Function Name : HSM_DbgIfChal
 *** Description   : This function obtains a random number(256bits)
 ***                 and UID(120bits) which the user shall use along with the
 ***                 SOC_DEBUG_KEY/HSM_DEBUG_KEY return an authorization request
 ******************************************************************************/
errcode_t HSM_DbgIfChal(uint8_t* challenge)
{
    ASSERT_PARAM(challenge != NULL);
    ASSERT_PARAM(s_hsmState != NULL);

    errcode_t errCode = ERR_SUCCESS;
    uint32_t cmdBuf[2U];
    volatile uint32_t timeout = CMD_RESPONSE_TIME_OUT;

    if (MBOX_REG_GetCommandIdleStatus() != true) {
        errCode = ERR_BUSY;
    } else {
        s_hsmState->cmdInProgress = true;

        cmdBuf[0] = CMD_HEAD_SEQUENCE + (uint32_t)HSM_CMD_DBG_IF_CHAL;
        cmdBuf[1] = (uint32_t)challenge;

        s_hsmState->cmd = HSM_CMD_DBG_IF_CHAL;

        /* Use polling to get command completion status without interrupts */
        MBOX_REG_CommandIntEnable(false);
        MBOX_REG_WriteCommand(cmdBuf, sizeof(cmdBuf));
        MBOX_REG_StartCommand();

        /* Wait for the command to complete */
        while ((MBOX_REG_GetCommandCompletionStatus() != true) && (timeout > 0)) {
            timeout--;
        }

        MBOX_REG_ClearCommandCompletionStatus();

        if (timeout == 0U) {
            errCode = ERR_TIMEOUT;
        } else {
            errCode = HSM_GetCommandExecStatus();
        }

        s_hsmState->cmdInProgress = false;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : HSM_DbgIfAuth
 *** Description   : This function enable the debug interface if the
 ***                 authorization is confirmed by HSM
 ******************************************************************************/
errcode_t HSM_DbgIfAuth(dbg_auth_type_t type,
                        crypto_alg_t alg,
                        const uint8_t* authorization)
{
    ASSERT_PARAM(authorization != NULL);
    ASSERT_PARAM(s_hsmState != NULL);

    errcode_t errCode = ERR_SUCCESS;
    uint32_t cmdBuf[4U];
    volatile uint32_t timeout = CMD_RESPONSE_TIME_OUT;

    if (MBOX_REG_GetCommandIdleStatus() != true) {
        errCode = ERR_BUSY;
    } else {
        s_hsmState->cmdInProgress = true;

        cmdBuf[0] = CMD_HEAD_SEQUENCE + (uint32_t)HSM_CMD_DBG_IF_AUTH;
        cmdBuf[1] = (uint32_t)type;
        cmdBuf[2] = (uint32_t)alg;
        cmdBuf[3] = (uint32_t)authorization;

        s_hsmState->cmd = HSM_CMD_DBG_IF_AUTH;

        /* Use polling to get command completion status without interrupts */
        MBOX_REG_CommandIntEnable(false);
        MBOX_REG_WriteCommand(cmdBuf, sizeof(cmdBuf));
        MBOX_REG_StartCommand();

        /* Wait for the command to complete */
        while ((MBOX_REG_GetCommandCompletionStatus() != true) && (timeout > 0)) {
            timeout--;
        }

        MBOX_REG_ClearCommandCompletionStatus();

        if (timeout == 0U) {
            errCode = ERR_TIMEOUT;
        } else {
            errCode = HSM_GetCommandExecStatus();
        }

        s_hsmState->cmdInProgress = false;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : HSM_GenerateHashOnePass
 *** Description   : This function calculates the hash digest in one-pass mode
 ******************************************************************************/
errcode_t HSM_GenerateHashOnePass(hash_alg_type_t hashAlg,
                                  const uint8_t* msg,
                                  uint32_t msgLen,
                                  uint8_t* digestOut,
                                  uint32_t timeout)
{
    ASSERT_PARAM(msg != NULL);
    ASSERT_PARAM(digestOut != NULL);
    ASSERT_PARAM(s_hsmState != NULL);

    /* Not used, avoid warning */
    (void)timeout;
    errcode_t errCode = ERR_SUCCESS;
    uint32_t cmdBuf[8U];

    if (MBOX_REG_GetCommandIdleStatus() != true) {
        errCode = ERR_BUSY;
    } else {
        s_hsmState->cmdInProgress = true;

        cmdBuf[0] = CMD_HEAD_SEQUENCE + (uint32_t)HSM_CMD_GENERATE_HASH;
        cmdBuf[1] = (uint32_t)PROCESS_MODE_ONEPASS;
        cmdBuf[2] = (uint32_t)hashAlg;
        cmdBuf[3] = (uint32_t)msg;
        cmdBuf[4] = msgLen;
        cmdBuf[5] = (uint32_t)digestOut;
        cmdBuf[6] = 0U; /* no use */
        cmdBuf[7] = 0U; /* no use */

        s_hsmState->cmd = HSM_CMD_GENERATE_HASH;

        MBOX_REG_CommandIntEnable(true);
        MBOX_REG_WriteCommand(cmdBuf, sizeof(cmdBuf));
        MBOX_REG_StartCommand();

        /* Wait for the command to complete */
        errCode = OS_Adapter_WaitSemaphore(&(s_hsmState->cmdSemaphore), OS_ADAPTER_WAIT_FOREVER);

        if (errCode == ERR_SUCCESS) {
            errCode = HSM_GetCommandExecStatus();
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : HSM_GenerateHashStart
 *** Description   : This function calculates the hash digest in start mode
 ******************************************************************************/
errcode_t HSM_GenerateHashStart(hash_alg_type_t hashAlg,
                                uint8_t* ctxBuf,
                                uint32_t ctxBufLen,
                                uint32_t timeout)
{
    ASSERT_PARAM(ctxBuf != NULL);
    ASSERT_PARAM(s_hsmState != NULL);

    /* Not used, avoid warning */
    (void)timeout;
    errcode_t errCode = ERR_SUCCESS;
    uint32_t cmdBuf[8U];

    if (MBOX_REG_GetCommandIdleStatus() != true) {
        errCode = ERR_BUSY;
    } else {
        s_hsmState->cmdInProgress = true;

        cmdBuf[0] = CMD_HEAD_SEQUENCE + (uint32_t)HSM_CMD_GENERATE_HASH;
        cmdBuf[1] = (uint32_t)PROCESS_MODE_START;
        cmdBuf[2] = (uint32_t)hashAlg;
        cmdBuf[3] = 0U; /* no use */
        cmdBuf[4] = 0U; /* no use */
        cmdBuf[5] = 0U; /* no use */
        cmdBuf[6] = (uint32_t)ctxBuf;
        cmdBuf[7] = ctxBufLen;

        s_hsmState->cmd = HSM_CMD_GENERATE_HASH;

        MBOX_REG_CommandIntEnable(true);
        MBOX_REG_WriteCommand(cmdBuf, sizeof(cmdBuf));
        MBOX_REG_StartCommand();

        /* Wait for the command to complete */
        errCode = OS_Adapter_WaitSemaphore(&(s_hsmState->cmdSemaphore), OS_ADAPTER_WAIT_FOREVER);

        if (errCode == ERR_SUCCESS) {
            errCode = HSM_GetCommandExecStatus();
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : HSM_GenerateHashUpdate
 *** Description   : This function calculates the hash digest in update mode
 ******************************************************************************/
errcode_t HSM_GenerateHashUpdate(hash_alg_type_t hashAlg,
                                 const uint8_t* msg,
                                 uint32_t msgLen,
                                 uint8_t* ctxBuf,
                                 uint32_t ctxBufLen,
                                 uint32_t timeout)
{
    ASSERT_PARAM(ctxBuf != NULL);
    ASSERT_PARAM(msg != NULL);
    ASSERT_PARAM(s_hsmState != NULL);

    /* Not used, avoid warning */
    (void)timeout;
    errcode_t errCode = ERR_SUCCESS;
    uint32_t cmdBuf[8U];

    if (MBOX_REG_GetCommandIdleStatus() != true) {
        errCode = ERR_BUSY;
    } else {
        s_hsmState->cmdInProgress = true;

        cmdBuf[0] = CMD_HEAD_SEQUENCE + (uint32_t)HSM_CMD_GENERATE_HASH;
        cmdBuf[1] = (uint32_t)PROCESS_MODE_UPDATE;
        cmdBuf[2] = (uint32_t)hashAlg;
        cmdBuf[3] = (uint32_t)msg;
        cmdBuf[4] = msgLen;
        cmdBuf[5] = 0U; /* no use */
        cmdBuf[6] = (uint32_t)ctxBuf;
        cmdBuf[7] = ctxBufLen;

        s_hsmState->cmd = HSM_CMD_GENERATE_HASH;

        MBOX_REG_CommandIntEnable(true);
        MBOX_REG_WriteCommand(cmdBuf, sizeof(cmdBuf));
        MBOX_REG_StartCommand();

        /* Wait for the command to complete */
        errCode = OS_Adapter_WaitSemaphore(&(s_hsmState->cmdSemaphore), OS_ADAPTER_WAIT_FOREVER);

        if (errCode == ERR_SUCCESS) {
            errCode = HSM_GetCommandExecStatus();
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : HSM_GenerateHashFinish
 *** Description   : This function calculates the hash digest in Finish mode
 ******************************************************************************/
errcode_t HSM_GenerateHashFinish(hash_alg_type_t hashAlg,
                                 uint8_t* digestOut,
                                 uint8_t* ctxBuf,
                                 uint32_t ctxBufLen,
                                 uint32_t timeout)
{
    ASSERT_PARAM(ctxBuf != NULL);
    ASSERT_PARAM(digestOut != NULL);
    ASSERT_PARAM(s_hsmState != NULL);

    /* Not used, avoid warning */
    (void)timeout;
    errcode_t errCode = ERR_SUCCESS;
    uint32_t cmdBuf[8U];

    if (MBOX_REG_GetCommandIdleStatus() != true) {
        errCode = ERR_BUSY;
    } else {
        s_hsmState->cmdInProgress = true;

        cmdBuf[0] = CMD_HEAD_SEQUENCE + (uint32_t)HSM_CMD_GENERATE_HASH;
        cmdBuf[1] = (uint32_t)PROCESS_MODE_FINISH;
        cmdBuf[2] = (uint32_t)hashAlg;
        cmdBuf[3] = 0U; /* no use */
        cmdBuf[4] = 0U; /* no use */
        cmdBuf[5] = (uint32_t)digestOut;
        cmdBuf[6] = (uint32_t)ctxBuf;
        cmdBuf[7] = ctxBufLen;

        s_hsmState->cmd = HSM_CMD_GENERATE_HASH;

        MBOX_REG_CommandIntEnable(true);
        MBOX_REG_WriteCommand(cmdBuf, sizeof(cmdBuf));
        MBOX_REG_StartCommand();

        /* Wait for the command to complete */
        errCode = OS_Adapter_WaitSemaphore(&(s_hsmState->cmdSemaphore), OS_ADAPTER_WAIT_FOREVER);

        if (errCode == ERR_SUCCESS) {
            errCode = HSM_GetCommandExecStatus();
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : HSM_RsaPssVeirfySign
 *** Description   : This function verification signature in RSA-PASS mode
 ******************************************************************************/
errcode_t HSM_RsaPssVerifySign(hash_alg_type_t hashAlg,
                               uint32_t saltLen,
                               const uint8_t* digest,
                               const uint8_t* signature,
                               const rsa_public_key_type_t* publicKey,
                               uint8_t* ctxBuf,
                               uint32_t ctxBufLen,
                               uint32_t timeout)
{
    ASSERT_PARAM(digest != NULL);
    ASSERT_PARAM(signature != NULL);
    ASSERT_PARAM(publicKey != NULL);
    ASSERT_PARAM(s_hsmState != NULL);

    /* Not used, avoid warning */
    (void)timeout;
    errcode_t errCode = ERR_SUCCESS;
    uint32_t cmdBuf[8U];

    if (MBOX_REG_GetCommandIdleStatus() != true) {
        errCode = ERR_BUSY;
    } else {
        s_hsmState->cmdInProgress = true;

        cmdBuf[0] = CMD_HEAD_SEQUENCE + (uint32_t)HSM_CMD_VERIFY_SIGN;
        cmdBuf[1] = (uint32_t)hashAlg;
        cmdBuf[2] = saltLen;
        cmdBuf[3] = (uint32_t)digest;
        cmdBuf[4] = (uint32_t)signature;
        cmdBuf[5] = (uint32_t)publicKey;
        cmdBuf[6] = (uint32_t)ctxBuf;
        cmdBuf[7] = ctxBufLen;

        s_hsmState->cmd = HSM_CMD_VERIFY_SIGN;

        MBOX_REG_CommandIntEnable(true);
        MBOX_REG_WriteCommand(cmdBuf, sizeof(cmdBuf));
        MBOX_REG_StartCommand();

        /* Wait for the command to complete */
        errCode = OS_Adapter_WaitSemaphore(&(s_hsmState->cmdSemaphore), OS_ADAPTER_WAIT_FOREVER);

        if (errCode == ERR_SUCCESS) {
            errCode = HSM_GetCommandExecStatus();
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : HSM_DbgIfAccessIsAllowed
 *** Description   : This function returns the status of whether the
 ***                 debug interface is available
 ******************************************************************************/
bool HSM_DbgIfAccessIsAllowed(dbg_auth_type_t type)
{
    bool ret = false;
    uint32_t status;

    status = HSM_REG_GetDebugStatus();

    if (type == AUTH_TYPE_SOC_DEBUG) {
        if (status & HSM_DBGSTA_SOCDBG_Msk) {
            ret = true;
        }
    } else {
        if (status & HSM_DBGSTA_HSMDBG_Msk) {
            ret = true;
        }
    }

    return ret;
}

/*******************************************************************************
 *** Function Name : HSM_GetHsmEnterWfiStatus
 *** Description   : This function returns the status of whether the
 ***                 HSM entered the wfi
 ******************************************************************************/
bool HSM_GetHsmEnterWfiStatus(void)
{
    bool ret = false ;
    uint32_t status;

    status = HSM_REG_GetMboxStatus0();

    if (status & MBOX_STATUS0_WFI_Msk) {
        ret = true;
    }

    return ret;
}

/*******************************************************************************
 *** Function Name : HSM_GetHsmFwInfo
 *** Description   : Get HSM firmware boot status and version counter
 ******************************************************************************/
void HSM_GetHsmFwInfo(hsm_fw_info_t* fw)
{
    uint32_t status = HSM_REG_GetMboxStatus0();

    fw->fwDone = (status & MBOX_STATUS0_FWBD_Msk) ? true : false;
    fw->fwError = (status & MBOX_STATUS0_FWBE_Msk) ? true : false;
    fw->fwVersion = HSM_REG_GetFwStatus1();
}

/*******************************************************************************
 *** Function Name : HSM_InstallCallback
 *** Description   : Installs a callback function which will be invoked when an
 ***                 asynchronous command finishes its execution
 ******************************************************************************/
void HSM_InstallCallback(mailbox_callback_t callbackFunc, void* callbackParam)
{
    ASSERT_PARAM(s_hsmState != NULL);

    s_hsmState->callback = callbackFunc;
    s_hsmState->callbackParam = callbackParam;
}

/*******************************************************************************
 *** Function Name : MBOX_IRQHandlerProcess
 *** Description   : MOBX interrupt handler process
 ******************************************************************************/
void MBOX_IRQHandlerProcess(void)
{
    ASSERT_PARAM(s_hsmState != NULL);

    if (MBOX_REG_GetCommandIntFlag() == true) {
        MBOX_REG_CommandIntEnable(false);
        MBOX_REG_ClearCommandCompletionStatus();

        (void)OS_Adapter_PostSemaphore(&(s_hsmState->cmdSemaphore));

        if (s_hsmState->callback != NULL) {
            s_hsmState->callback((uint32_t)s_hsmState->cmd, \
                                 s_hsmState->callbackParam);
        }

        s_hsmState->cmdInProgress = false;
    }
}

/*******************************************************************************
 *** Function Name : HSM_GetCommandExecStatus
 *** Description   : This function reads the error bits reported after running
 ***                 a HSM command
 ******************************************************************************/
static errcode_t HSM_GetCommandExecStatus(void)
{
    uint32_t stat;

    stat = MBOX_REG_GetCommandResponse();

    return (errcode_t)stat;
}

/*******************************************************************************
 *** Function Name : FLASH_SafetyLock
 *** Description   : Safety lock flash control
 ******************************************************************************/
static void FLASH_SafetyLock(void)
{
#define FLASH_MASTER_ACCESS_CONTROL_KEY                (0x5A1234A5U)
    FLASH->MACR = FLASH_MASTER_ACCESS_CONTROL_KEY;
    FLASH->DATAH = 0xFFFFFFFFU;
    FLASH->DATAL = 0xFFFFFFFFU;
}

/*******************************************************************************
 *** Function Name : FLASH_SafetyUnlock
 *** Description   : Safety unlock flash control
 ******************************************************************************/
static void FLASH_SafetyUnlock(void)
{
    uint32_t timeout = 0x1000U;

    FLASH->CCR = 0U;
    FLASH->ADDR = 1U;
    FLASH->SR = 1U;

    while (((FLASH->SR & FLASH_SR_CCF_Msk) == 0U) && (timeout > 0U)) {
        timeout--;
    }

    FLASH->SR = 0xE2U;
}

/*******EOF********************************************************************/
